package com.legal_documents_backend.service;

import com.legal_documents_backend.dto.*;
import com.legal_documents_backend.entity.Case;
import lombok.AllArgsConstructor;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.neo4j.driver.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CaseNeo4jService {
    @Autowired
    private Neo4jClient neo4jClient;

    private static final Map<String, RelationshipFilterConfig> RELATIONSHIP_FILTERS = Map.of(
            "region",    new RelationshipFilterConfig("r", "Region", "name", "位于"),
            "caseType",  new RelationshipFilterConfig("ct", "CaseType", "name", "属于"),
            "legalBasis",new RelationshipFilterConfig("lb", "LegalBasis", "name", "法律依据为"),
            "openTime",  new RelationshipFilterConfig("ot", "OpenTime", "time", "开庭时间")
    );

    @AllArgsConstructor
    private static class RelationshipFilterConfig {
        String alias;
        String label;
        String property;
        String relationshipType;
    }

//    public List<FilteredCaseDTO> getFilteredCases(CaseFilterRequest filters) {
//        StringBuilder cypher = new StringBuilder("MATCH (case:Case) ");
//        Map<String, Object> params = new HashMap<>();
//        List<String> whereClauses = new ArrayList<>();
//
//        for (Field field : filters.getClass().getDeclaredFields()) {
//            try {
//                field.setAccessible(true);
//                Object value = field.get(filters);
//                if (value == null) continue;
//
//                String key = field.getName();
//                if (RELATIONSHIP_FILTERS.containsKey(key)) {
//                    RelationshipFilterConfig config = RELATIONSHIP_FILTERS.get(key);
//                    cypher.append(String.format(
//                            "MATCH (case)-[:%s]->(%s:%s) ",
//                            config.relationshipType, config.alias, config.label
//                    ));
//                    whereClauses.add(String.format("%s.%s = $%s", config.alias, config.property, key));
//                } else {
//                    whereClauses.add(String.format("case.%s = $%s", key, key));
//                }
//                params.put(key, value);
//            } catch (IllegalAccessException e) {
//                throw new RuntimeException("反射访问字段失败", e);
//            }
//        }
//
//        if (!whereClauses.isEmpty()) {
//            cypher.append("WHERE ").append(String.join(" AND ", whereClauses)).append(" ");
//        }
//        cypher.append("RETURN case");
//
//        return (List<FilteredCaseDTO>) neo4jClient.query(cypher.toString())
//                .bindAll(params)
//                .fetchAs(FilteredCaseDTO.class)
//                .mappedBy((typeSystem, record) -> {
//                    Node caseNode = record.get("case").asNode();
//                    return convertNodeToDTO(caseNode);
//                })
//                .all();
//    }

    public List<CaseRelationsDTO> getFilteredCasesWithRelations(CaseFilterRequest filters) {
        StringBuilder cypher = new StringBuilder("MATCH (case:Case) ");
        Map<String, Object> params = new HashMap<>();
        List<String> whereClauses = new ArrayList<>();

        for (Field field : filters.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                Object value = field.get(filters);
                if (value == null) continue;

                String key = field.getName();
                if (RELATIONSHIP_FILTERS.containsKey(key)) {
                    RelationshipFilterConfig config = RELATIONSHIP_FILTERS.get(key);
                    cypher.append(String.format(
                            "MATCH (case)-[:%s]->(%s:%s) ",
                            config.relationshipType, config.alias, config.label
                    ));
                    whereClauses.add(String.format("%s.%s = $%s", config.alias, config.property, key));
                } else {
                    whereClauses.add(String.format("case.%s = $%s", key, key));
                }
                params.put(key, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("反射访问字段失败", e);
            }
        }

        if (!whereClauses.isEmpty()) {
            cypher.append("WHERE ").append(String.join(" AND ", whereClauses)).append(" ");
        }

        cypher.append("OPTIONAL MATCH (case)-[rel]->(related) ");
        cypher.append("RETURN case, collect(rel) AS relations, collect(related) AS relatedNodes");

        return neo4jClient.query(cypher.toString())
                .bindAll(params)
                .fetchAs(CaseRelationsDTO.class)
                .mappedBy((typeSystem, record) -> {
                    Node caseNode = record.get("case").asNode();

                    List<Map<String, Object>> relations = record.get("relations")
                            .asList(Value::asRelationship)
                            .stream()
                            .map(rel -> {
                                Map<String, Object> relMap = new HashMap<>();
                                relMap.put("type", rel.type());
                                relMap.put("properties", rel.asMap());
                                return relMap;
                            })
                            .collect(Collectors.toList());

                    List<Map<String, Object>> relatedNodes = record.get("relatedNodes")
                            .asList(Value::asNode)
                            .stream()
                            .map(node -> {
                                Map<String, Object> nodeMap = new HashMap<>();
                                nodeMap.put("labels", node.labels());
                                nodeMap.put("properties", node.asMap());
                                return nodeMap;
                            })
                            .collect(Collectors.toList());

                    return convertToCaseRelationsDTO(caseNode, relations, relatedNodes);
                })
                .all()
                .stream()
                .toList();
    }


    private FilteredCaseDTO convertNodeToDTO(Node caseNode) {
        List<String> labels = new ArrayList<>();
        for (String label : caseNode.labels()) {
            labels.add(label);
        }

        return new FilteredCaseDTO(
                caseNode.elementId(),
                labels,
                caseNode.asMap()
        );
    }

    public CaseRelationsDTO getCaseRelations(Integer caseId) {
        String cypher =
                "MATCH (c:Case {case_id: $caseId})-[r]->(related) " +
                        "RETURN c AS caseNode, " +
                        "collect({type: type(r), properties: properties(r)}) AS relations, " +
                        "collect({labels: labels(related), properties: properties(related)}) AS relatedNodes";

        return neo4jClient.query(cypher)
                .bind(caseId).to("caseId")
                .fetchAs(CaseRelationsDTO.class)
                .mappedBy((typeSystem, record) -> {
                    Node caseNode = record.get("caseNode").asNode();
                    List<Map<String, Object>> relations = (List<Map<String, Object>>) (Object) record.get("relations").asList();
                    List<Map<String, Object>> relatedNodes = (List<Map<String, Object>>) (Object) record.get("relatedNodes").asList();
//                    List<Relationship> relations = record.get("relations").asList(Value::asRelationship);
//                    List<Node> relatedNodes = record.get("relatedNodes").asList(Value::asNode);

                    return convertToCaseRelationsDTO(caseNode, relations, relatedNodes);
                })
                .one()
                .orElseThrow(() -> new RuntimeException("未找到数据"));
    }

    private CaseRelationsDTO convertToCaseRelationsDTO(
            Node caseNode,
            List<Map<String, Object>> relations,
            List<Map<String, Object>> relatedNodes
    ) {
        CaseRelationsDTO dto = new CaseRelationsDTO();

        Case caseDetail = new Case();
        caseDetail.setId(caseNode.id());
        caseDetail.setCaseId(caseNode.get("case_id").asInt());
        caseDetail.setCaseNumber(caseNode.get("case_number").asString());
        caseDetail.setCaseName(caseNode.get("case_name").asString());
        caseDetail.setParty(caseNode.get("party").asString());
        caseDetail.setUrl(caseNode.get("url").asString());
        caseDetail.setSource(caseNode.get("source").asString());
        caseDetail.setHearingProcedure(caseNode.get("hearing_procedure").asString());
        caseDetail.setCauseOfAction(caseNode.get("cause_of_action").asString());
        caseDetail.setAdjudicationTime(caseNode.get("adjudication_time").asLocalDate());
        dto.setCaseNode(caseDetail);

        List<RelationDTO> relationDTOs = relations.stream()
                .map(rel -> {
                    RelationDTO rdto = new RelationDTO();
                    rdto.setType((String) rel.get("type"));
                    rdto.setProperties((Map<String, Object>) rel.get("properties"));
                    return rdto;
                }).collect(Collectors.toList());
        dto.setRelations(relationDTOs);

        List<NodeDTO> nodeDTOs = relatedNodes.stream()
                .map(node -> {
                    NodeDTO ndto = new NodeDTO();
                    ndto.setLabels((List<String>) node.get("labels"));
                    ndto.setProperties((Map<String, Object>) node.get("properties"));
                    return ndto;
                }).collect(Collectors.toList());
        dto.setRelatedNodes(nodeDTOs);

        return dto;
    }

}
